﻿using AutoMapper;
using ManagementApi.Authorizers;
using ManagementApi.Models;
using ManagementApi.Models.Manage.ProjectCorp;
using ManagementApi.Models.Manage.ProjectGroupMember;
using ManagementApi.Models.Payroll;
using ManagementApi.Models.Person;
using ManagementApi.Models.System.AuditLog;
using ManagementApi.Mvc;
using ManagementApi.Mvc.ApplicationContexts;
using ManagementApi.Mvc.Filters;
using ManagementApi.Services;
using ManagementApi.Services.BankTransfer;
using ManagementApi.Services.Import;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Modules;
using Modules.Attachment;
using Modules.Audit;
using Modules.BankSpecialAccount;
using Modules.Common;
using Modules.Corp;
using Modules.Payroll;
using Modules.Person;
using Modules.Project;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using PmSoft;
using PmSoft.Exceptions;
using PmSoft.Utilities;
using System.ComponentModel.DataAnnotations;
using static Modules.Payroll.PayrollConstants;
using static Modules.Payroll.WageConstants;
using static Modules.Person.PersonConstants;
using static Modules.System.Flow.FlowConstants;

namespace ManagementApi.Controllers.ManagerPannel
{
    /// <summary>
    /// 工资单相关接口
    /// </summary>
    [Route("/api/v1/manager/payroll")]
    [ApiController]
    [Authorize(Roles = "Manager")]
    [TypeFilter(typeof(RequireSelProjectActionFilter))]
    public class PayrollController : ControllerBase
    {
        private readonly WagePayrollService wagePayrollService;
        private readonly ProjectManagerApplicationContext applicationContext;
        private readonly SysAuditLogService sysAuditLogService;
        private readonly SerialNumberService serialNumberService;
        private readonly ProjectService projectService;
        private readonly ProjectGroupMemberService projectGroupMemberService;
        private readonly ImportService importService;
        private readonly WagePayrollDetailService payrollDetailService;
        private readonly IMapper mapper;
        private readonly ProjectInfo? projectInfo;
        private readonly ExportService exportService;
        private readonly ProjectCorpWageAuthService projectCorpWageAuthService;
        private readonly BankSpecialAccountService projectSpecialAccountService;
        private readonly BankTransferService bankTransferService;
        private readonly ProjectOwnAuthorizer ownAuthorizer;
        private readonly WageAttachmentService wageAttachmentService;

        /// <summary>
        /// </summary>
        /// <param name="wagePayrollService"></param>
        /// <param name="payrollDetailService"></param>
        /// <param name="applicationContext"></param>
        /// <param name="sysAuditLogService"></param>
        /// <param name="importService"></param>
        /// <param name="serialNumberService"></param>
        /// <param name="projectGroupMemberService"></param>
        /// <param name="projectService"></param>
        /// <param name="exportService"></param>
        /// <param name="mapper"></param>
        /// <param name="projectCorpWageAuthService"></param>
        /// <param name="ownAuthorizer"></param>
        /// <exception cref="BusinessException"></exception>
        public PayrollController(WagePayrollService wagePayrollService, WagePayrollDetailService payrollDetailService, ProjectManagerApplicationContext applicationContext, SysAuditLogService sysAuditLogService, ImportService importService, SerialNumberService serialNumberService, ProjectGroupMemberService projectGroupMemberService, ProjectService projectService, ExportService exportService, IMapper mapper, ProjectCorpWageAuthService projectCorpWageAuthService, BankSpecialAccountService projectSpecialAccountService, BankTransferService bankTransferService, ProjectOwnAuthorizer ownAuthorizer, WageAttachmentService wageAttachmentService)
        {
            this.wagePayrollService = wagePayrollService;
            this.payrollDetailService = payrollDetailService;
            this.applicationContext = applicationContext;
            this.sysAuditLogService = sysAuditLogService;
            this.serialNumberService = serialNumberService;
            this.projectService = projectService;
            this.projectGroupMemberService = projectGroupMemberService;
            this.exportService = exportService;
            this.importService = importService;
            this.mapper = mapper;
            projectInfo = projectService.GetProjectInfo(applicationContext.CurrentProjectInfo.ProjectId);
            this.projectCorpWageAuthService = projectCorpWageAuthService;
            this.ownAuthorizer = ownAuthorizer;
            this.projectSpecialAccountService = projectSpecialAccountService;
            this.bankTransferService = bankTransferService;
            this.wageAttachmentService = wageAttachmentService;
        }

        /// <summary>
        /// 新增工资单
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        [HttpPost("create")]
        public ApiResult CreateWagePayroll(PayrollEditForm form)
        {
            if (form.WageType == 工资单类型.非正常工资单)
            {
                ProjectCorpWageAuth? projectCorpWageAuth = projectCorpWageAuthService.GetProjectCorpWageAuthByProjectIdAndCorpIdAndMonth(applicationContext.CurrentProjectInfo.ProjectId, form.CorpId, form.Month);
                if (projectCorpWageAuth == null)
                    return ApiResult.Failure($"对不起，当前参建企业没有新增{form.Month.ToString()}非正常工资单权限");
            }
            WagePayroll entity = new();
            mapper.Map(form, entity);
            ProjectInfo? projectInfo = projectService.GetProjectInfo(applicationContext.CurrentProjectInfo.ProjectId);
            if (projectInfo == null) return ApiResult.Failure("项目信息不存在");
            var numbers = serialNumberService.GetNumAndSerialNumber(SerialNumberType.工资单, projectInfo.PrjNum, projectInfo.ProjectId);
            entity.SerialNumber = numbers.SerialNumber;
            entity.Code = numbers.Code;
            entity.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            entity.CreateTime = DateTime.Now;
            wagePayrollService.Create(entity, FlowEngine.项目管理员提交, 项目管理员提交工资单流程);
            return ApiResult.Success("创建成功");
        }

        /// <summary>
        /// 编辑工资单
        /// </summary>
        /// <param name="id"></param>
        /// <param name="form"></param>
        /// <returns></returns>
        [HttpPut("{id}")]
        public ApiResult UpdateWagePayroll(int id, PayrollEditForm form)
        {
            WagePayroll? data = wagePayrollService.Get(id);
            if (data == null) return ApiResult.Failure("工资单不存在");

            #region 数据权限验证

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(data.ProjectId);

            #endregion 数据权限验证

            mapper.Map(form, data);
            wagePayrollService.UpdateWagePayRoll(data);
            return ApiResult.Success("修改成功");
        }

        /// <summary>
        /// 工资单表单数据
        /// </summary>
        /// <param name="id">角色ID</param>
        /// <returns></returns>
        [HttpGet("{id}/form")]
        public ApiResult<PayrollEditForm> GetWagePayroll(int id)
        {
            PayrollEditForm form = new PayrollEditForm();
            if (id == 0)
            {
                return ApiResult<PayrollEditForm>.Success(form);
            }
            WagePayroll? data = wagePayrollService.Get(id);
            if (data == null)
                return ApiResult<PayrollEditForm>.Failure("工资单不存在");
            mapper.Map(data, form);
            form.CorpName = applicationContext.GetService<CorpService>().GetCorpInfo(data.CorpId)?.Name;
            if (form.EndDate.Date == DateTime.Now.Date)
            {
                form.EndDate = DateTime.Now.AddDays(-1).Date;
                form.Name = $"【{form.StartDate.ToString("yyyy-MM-01")}至{form.EndDate.ToString("yyyy-MM-dd")}_按{form.PayType.GetDescription()}发】工资单";
            }
            return ApiResult<PayrollEditForm>.Success(form);
        }

        /// <summary>
        /// 获取工资单列表(分页)
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="pageNum">页码</param>
        /// <param name="pageSize">每页数量</param>
        /// <returns></returns>
        [HttpGet("page")]
        public ApiResult<PageDto<PayrollDto>> GetWagePayrolls([FromQuery] WagePayrollQuery query, int pageNum = 1, int pageSize = 20)
        {
            if (!query.ProjectId.HasValue) query.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            var data = wagePayrollService.GetWagePayrolls(query, pageNum, pageSize);
            var dtos = data.AsPageDto(source => { return source.AsDto(FlowEngine.项目管理员提交, FlowEngine.项目管理员审核); });
            return ApiResult<PageDto<PayrollDto>>.Success(dtos);
        }

        /// <summary>
        /// 删除工资单
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        [HttpDelete("{ids}")]
        public ApiResult Delete(string ids)
        {
            #region 数据权限验证

            var data = wagePayrollService.GetWagePayrolls(ValueUtility.ConvertToList<int>(ids));
            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(data.Select(i => i.ProjectId));

            #endregion 数据权限验证

            wagePayrollService.Deleted(ValueUtility.ConvertToList<int>(ids));
            return ApiResult.Success("删除成功");
        }

        /// <summary>
        /// 提交工资单信息
        /// </summary>
        /// <param name="id"></param>
        /// <param name="form"></param>
        /// <returns></returns>
        /// <exception cref="BusinessException"></exception>
        [HttpPost("submit/{id}")]
        public ApiResult Submit(int id, PayrollSubmitForm form)
        {
            string flowCode = 项目管理员提交工资单流程;
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");
            if (entity.WageType == 工资单类型.非正常工资单) flowCode = 项目管理员提交非正常工资单流程;

            #region 数据权限验证

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 数据权限验证

            if (entity.NumberPeople == 0)
                throw new BusinessException("请录入人员工资信息后提交");
            if (entity.AuditType != 审核类型.待提交)
                throw new BusinessException("审核类型为待提交才能提交");
            entity.AuditType = 审核类型.待审核;
            wagePayrollService.SubmitAudit(entity, flowCode, form.attachments.Select(a => a.AttachmentId));
            return ApiResult.Success("提交成功");
        }

        /// <summary>
        /// 查询审核日志
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("auditLogs")]
        public ApiResult<IEnumerable<AuditLogDto>> GetSysAuditLogs(int id)
        {
            var data = sysAuditLogService.GetAuditLogsByObjectId(id, "wage_payroll");
            IEnumerable<AuditLogDto> auditLogDtos = data.AsDto();
            return ApiResult<IEnumerable<AuditLogDto>>.Success(auditLogDtos);
        }

        /// <summary>
        /// 审核
        /// </summary>
        /// <param name="id"></param>
        /// <param name="auditForm"></param>
        /// <returns></returns>
        [HttpPut("audit/{id}")]
        public ApiResult Audit(int id, AuditForm auditForm)
        {
            #region 数据权限验证

            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");
            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 数据权限验证

            wagePayrollService.Audit(id, auditForm.AuditIdea, auditForm.AuditIdeaContent, FlowEngine.项目管理员审核);
            return ApiResult.Success("审核成功");
        }

        /// <summary>
        /// 工资单明细数据
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("details/{id}")]
        public ApiResult<PayrollDetailDto> Details(int id)
        {
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");

            #region 数据权限验证

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 数据权限验证

            IEnumerable<WagePayrollDetail> details = payrollDetailService.GetPayrollDetailsByPayrollId(id);
            PayrollDetailDto dto = new();
            if (!details.Any()) {
                //无明细数据,查询当前项目参建企业的班组及成员
                dto = projectGroupMemberService.GetProjectGroupMembersByCorpIdsAndProjectId(ValueUtility.ConvertToList<int>(entity.CorpId.ToString()), applicationContext.CurrentProjectInfo.ProjectId).Where(a => a.StartDate <= entity.EndDate).AsProjectGroupMemberDtos().AsPayrollDetailDto(entity);
                dto.Details = dto.Details.Where(o => o.AttendanceDays > 0);//只返回 考勤天数大于0的
            }
            else
            {
                //有明细数据
                dto = details.AsPayrollDetailDto(entity, applicationContext);

                #region 附件查询

                var attachments = wageAttachmentService.GetSysAttachmentByObjectIdAndType(id, 工资附件类型.工资单凭证附件, 工资附件租户.WagePayroll);
                List<AttachmentsForm<int>> attach = new();
                foreach (var item in attachments)
                {
                    AttachmentsForm<int> attachmentsForm = new()
                    {
                        AttachmentId = item.Id,
                        AttachmentName = item.FriendlyName,
                    };
                    attach.Add(attachmentsForm);
                }
                dto.Attachments = attach;

                #endregion 附件查询
            }
            return ApiResult<PayrollDetailDto>.Success(dto);
        }

        /// <summary>
        /// 导出工资明细模板
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("exportTemplate/{id}")]
        public IActionResult ExportTemplate(int id)
        {
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");
            wagePayrollService.Verify(entity);
            PayrollDetailDto dto = new();
            //查询当前项目参建企业的班组及成员
            var data = projectGroupMemberService.GetProjectGroupMembersByCorpIdsAndProjectId(ValueUtility.ConvertToList<int>(entity.CorpId.ToString()), applicationContext.CurrentProjectInfo.ProjectId);
            data = data.Where(a => a.StartDate <= entity.EndDate);
            dto = data.AsProjectGroupMemberDtos().AsPayrollDetailDto(entity);

            #region 导出部分

            // 创建一个新的Excel工作簿
            IWorkbook workbook = new XSSFWorkbook();

            // 创建一个新的工作表
            ISheet sheet = workbook.CreateSheet("Sheet1");
            // 创建列并设置最小宽度
            int columnWidth = 20;
            int columnIndex = 0;
            sheet.SetColumnWidth(columnIndex, columnWidth * 256); // 设定列宽度，参数为1/256个字符宽度

            // 自动调整列宽
            for (int i = 0; i < 8; i++)
            {
                // 自动调整列宽
                //sheet.AutoSizeColumn(i);
                sheet.SetColumnWidth(i, Math.Max(columnWidth * 256, sheet.GetColumnWidth(i)));
            }

            #region 表头

            // 合并行和列的方式设置单元格的边界
            CellRangeAddress region = new CellRangeAddress(0, 1, 0, 7);
            sheet.AddMergedRegion(region);
            // 创建一个新的行
            IRow row = sheet.CreateRow(0);
            ICell cell = row.CreateCell(0);
            // 创建字体对象
            IFont font = workbook.CreateFont();
            font.FontHeightInPoints = 20; // 设置字体大小为 12

            // 设置单元格样式
            ICellStyle cellStyle = workbook.CreateCellStyle();
            cellStyle.Alignment = HorizontalAlignment.Center;//居中
            cellStyle.SetFont(font);
            cell.CellStyle = cellStyle;
            // 设置单元格的值
            cell.SetCellValue($"{dto.ProjectName}{dto.Month.ToString().Substring(0, 4)}年{dto.Month.ToString().Substring(4)}月工资明细表");

            #endregion 表头

            #region 中间部分

            exportService.CellRange(sheet, workbook, 4, 0, $"劳务公司:{dto.CorpName}", $"总支出额:{dto.TotalExpenditure}", $"当月应考勤天数:{dto.Days}");
            exportService.CellRange(sheet, workbook, 6, 0, $"当月计价总额:{dto.TotalValuation}", $"剩余合同金额:{dto.RemainingAmount}", $"流水号:{dto.Code}");

            #endregion 中间部分

            #region 表格部分

            exportService.CellRange(sheet, workbook, 8, 0, "项目名称", "劳务班组", "姓名", "身份证号", "开户行", "银行卡号", "实际考勤天数", "申请发放金额");

            #endregion 表格部分

            int index = 9;
            string projectName = "";
            foreach (var item in dto.Details)
            {
                exportService.CellRange(sheet, workbook, index, 0, item.ProjectName, item.GroupName, item.PersonName, item.IdCard, item.BankName, item.PersonBankNo, item.AttendanceDays.ToString(), "");
                projectName = item.ProjectName;
                index++;
            }

            // 将工作簿保存到内存流中
            using (MemoryStream memoryStream = new MemoryStream())
            {
                workbook.Write(memoryStream);

                // 设置要下载的文件名
                string fileName = $"{dto.CorpName}{projectName}{dto.Month.ToString().Substring(0, 4)}年{dto.Month.ToString().Substring(4)}月工资单模板.xlsx";

                // 返回文件内容结果
                return File(memoryStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
            }

            #endregion 导出部分
        }

        /// <summary>
        /// 导出明细
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("exportDetails/{id}")]
        public IActionResult ExportDetail(int id)
        {
            WagePayroll? wagePayroll = wagePayrollService.Get(id);
            if (wagePayroll == null) throw new BusinessException("工资单信息不存在，请先申请工资单");
            wagePayrollService.Verify(wagePayroll);
            IEnumerable<WagePayrollDetail> details = payrollDetailService.GetPayrollDetailsByPayrollId(id);
            PayrollDetailDto dto = new();
            if (!details.Any())
                //无明细数据,查询当前项目参建企业的班组及成员
                throw new BusinessException("工资单明细信息不存在，请先导入工资明细");
            else
                //有明细数据
                dto = details.AsPayrollDetailDto(wagePayroll, applicationContext);

            // 创建一个新的Excel工作簿
            IWorkbook workbook = new XSSFWorkbook();

            // 创建一个新的工作表
            ISheet sheet = workbook.CreateSheet("Sheet1");
            // 创建列并设置最小宽度
            int columnWidth = 20;
            int columnIndex = 0;
            sheet.SetColumnWidth(columnIndex, columnWidth * 256); // 设定列宽度，参数为1/256个字符宽度

            // 自动调整列宽
            for (int i = 0; i < 8; i++)
            {
                // 自动调整列宽
                //sheet.AutoSizeColumn(i);
                sheet.SetColumnWidth(i, Math.Max(columnWidth * 256, sheet.GetColumnWidth(i)));
            }
            int index = 4;

            #region 表头

            // 合并行和列的方式设置单元格的边界
            CellRangeAddress region = new CellRangeAddress(0, 1, 0, 7);
            sheet.AddMergedRegion(region);
            // 创建一个新的行
            IRow row = sheet.CreateRow(0);
            ICell cell = row.CreateCell(0);
            // 创建字体对象
            IFont font = workbook.CreateFont();
            font.FontHeightInPoints = 20; // 设置字体大小为 12

            // 设置单元格样式
            ICellStyle cellStyle = workbook.CreateCellStyle();
            cellStyle.Alignment = HorizontalAlignment.Center;//居中
            cellStyle.SetFont(font);
            cell.CellStyle = cellStyle;
            // 设置单元格的值
            cell.SetCellValue($"{dto.ProjectName}{dto.Month.ToString().Substring(0, 4)}年{dto.Month.ToString().Substring(4)}月工资审批表");

            #endregion 表头

            #region 中间部分

            exportService.CellRange(sheet, workbook, index, 0, $"劳务公司:{dto.CorpName}", $"总支出额:{dto.TotalExpenditure}", $"当月应考勤天数:{dto.Days}");
            index += 2;
            exportService.CellRange(sheet, workbook, index, 0, $"当月计价总额:{dto.TotalValuation}", $"当月申请发放额:{dto.AmountApplied}", $"剩余合同金额:{dto.RemainingAmount}");
            index++;
            exportService.CellRange(sheet, workbook, index, 0, $"当月申请代发班组列表:");
            index++;

            #endregion 中间部分

            #region 表头部分

            exportService.CellRange(sheet, workbook, index, 0, "序号", "班组名称", "工种", "工资单人数", "申请发放金额", "签字");
            index++;

            #endregion 表头部分

            #region 数据部分

            IEnumerable<PayrollDetailsDto> wagePayrollDetailDtos = dto.Details;
            var data = wagePayrollDetailDtos.GroupBy(a => new { a.GroupName, a.WorkType }).Select(g => new
            {
                GroupName = g.Key.GroupName,
                WorkType = g.Key.WorkType,
                PersonCount = g.Count(),
                AmountApplied = g.Sum(a => a.AmountApplied),
            });
            int rows = 1;
            foreach (var item in data)
            {
                exportService.CellRange(sheet, workbook, index, 0, rows.ToString(), item.GroupName, item.WorkType, item.PersonCount.ToString(), item.AmountApplied?.ToString() ?? string.Empty);
                index++;
                rows++;
            }

            #endregion 数据部分

            exportService.CellRange(sheet, workbook, index, 0, $"参建单位负责人签字：", $"计划合同部签字：");
            index++;
            exportService.CellRange(sheet, workbook, index, 0, $"财务部门签字：", $"项目经理签字：");
            index += 3;

            #region 表头

            // 合并行和列的方式设置单元格的边界
            CellRangeAddress region1 = new CellRangeAddress(index, index + 1, 0, 7);
            sheet.AddMergedRegion(region1);
            // 创建一个新的行
            row = sheet.CreateRow(index);
            cell = row.CreateCell(0);
            // 创建字体对象
            font = workbook.CreateFont();
            font.FontHeightInPoints = 20; // 设置字体大小为 12

            // 设置单元格样式
            cellStyle = workbook.CreateCellStyle();
            cellStyle.Alignment = HorizontalAlignment.Center;//居中
            cellStyle.SetFont(font);
            cell.CellStyle = cellStyle;
            // 设置单元格的值
            cell.SetCellValue($"{dto.ProjectName}{dto.Month.ToString().Substring(0, 4)}年{dto.Month.ToString().Substring(4)}月工资明细表");

            #endregion 表头

            index += 2;
            exportService.CellRange(sheet, workbook, index, 0, "项目名称", "劳务班组", "姓名", "身份证号", "开户行", "银行卡号", "实际考勤天数", "申请发放金额");

            #region 明细部分

            index++;
            decimal? personCountSun = 0;
            decimal? amountSum = 0;
            foreach (var item in dto.Details)
            {
                exportService.CellRange(sheet, workbook, index, 0, item.ProjectName, item.GroupName, item.PersonName, item.IdCard, item.BankName, item.PersonBankNo, item.AttendanceDays.ToString(), item.AmountApplied?.ToString() ?? string.Empty);
                personCountSun += item.AttendanceDays;
                amountSum += item.AmountApplied;
                index++;
            }
            exportService.CellRange(sheet, workbook, index, 0, "合计", "", "", "", "", "", personCountSun.ToString() ?? string.Empty, amountSum.ToString() ?? string.Empty);

            #endregion 明细部分

            // 将工作簿保存到内存流中
            using (MemoryStream memoryStream = new MemoryStream())
            {
                workbook.Write(memoryStream);

                // 设置要下载的文件名
                string fileName = $"{dto.CorpName}{dto.ProjectName}{dto.Month.ToString().Substring(0, 4)}年{dto.Month.ToString().Substring(4)}月工资单明细.xlsx";

                // 返回文件内容结果
                return File(memoryStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
            }
        }

        /// <summary>
        /// 导入工资单明细校验
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        [HttpPost("import")]
        public ApiResult<IEnumerable<PayrollDetailImportDto>> UploadExcel(IFormFile file)
        {
            IEnumerable<PayrollDetailImportDto> data = importService.VerifyPayrollExcelToList(applicationContext.CurrentProjectInfo.ProjectId, file);
            return ApiResult<IEnumerable<PayrollDetailImportDto>>.Success(data);
        }

        /// <summary>
        /// 新增工资单明细
        /// </summary>
        /// <param name="id"></param>
        /// <param name="forms"></param>
        /// <returns></returns>
        [HttpPost("addPayrollDetails/{id}")]
        public ApiResult AddWagePayrollDetails(int id, IEnumerable<PayrollDetailImportForm> forms)
        {
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");

            #region 数据权限验证

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 数据权限验证

            IEnumerable<WagePayrollDetail> payrollDetails = importService.VerifyPayrollDetailsToEntity(id, forms);
            payrollDetailService.Create(id, payrollDetails);
            return ApiResult.Success("操作成功");
        }

        /// <summary>
        /// 工资查询
        /// </summary>
        /// <param name="query"></param>
        /// <param name="pageNum"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        [HttpGet("page_salaryInquiry")]
        public ApiResult<PageDto<SalaryInquiryDto>> SalaryInquiry([FromQuery] WagePayrollQuery query, int pageNum = 1, int pageSize = 20)
        {
            query.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            var data = payrollDetailService.GetPayrollDetails(query, pageNum, pageSize);
            var dtos = data.AsPageDtos(source => { return source.AsDtos(applicationContext); });
            return ApiResult<PageDto<SalaryInquiryDto>>.Success(dtos);
        }

        /// <summary>
        /// 查询新增工资单权限
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        [HttpGet("wage_auth")]
        public ApiResult<bool> GetWageAuth([FromQuery] WagePayrollAuthQuery query)
        {
            if (query.WageType == 工资单类型.非正常工资单)
            {
                ProjectCorpWageAuth? projectCorpWageAuth = projectCorpWageAuthService.GetProjectCorpWageAuthByProjectIdAndCorpIdAndMonth(applicationContext.CurrentProjectInfo.ProjectId, query.CorpId, query.Month);
                if (projectCorpWageAuth == null)
                    return ApiResult<bool>.Success(false);
            }
            return ApiResult<bool>.Success(true);
        }

        /// <summary>
        /// 批量创建银行代发
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut("payment/create/{id}")]
        public async Task<ApiResult> CreatePaymentWages(int id)
        {
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");

            #region 验证数据权限

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 验证数据权限

            WagePayroll? wagePayroll = wagePayrollService.Get(id);
            if (wagePayroll == null) return ApiResult.Failure("工资单信息不存在，请先申请工资单");
            wagePayrollService.Verify(wagePayroll);
            IEnumerable<WagePayrollDetail> details = payrollDetailService.GetPayrollDetailsByPayrollId(id);
            if (!details.Any()) return ApiResult.Failure("工资明细数据不存在,请先完善工资明细数据");
            var account = projectSpecialAccountService.GetProjectSpecialAccountByProjectId(wagePayroll.ProjectId).Where(i => i.AccountStatus == BankSpecialAccountConstants.专户状态.正常).FirstOrDefault();
            if (account == null) return ApiResult.Failure("该项目专户不存在，请先完善专户信息");
            await bankTransferService.CreateBatchSalaryPaymentAsync(wagePayroll, details, account);
            return ApiResult.Success("创建银行代发单成功");
        }

        /// <summary>
        /// 提交创建的银行代发
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut("payment/submit/{id}")]
        public async Task<ApiResult> SubmitPaymentWages(int id)
        {
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");

            #region 验证数据权限

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 验证数据权限

            WagePayroll? wagePayroll = wagePayrollService.Get(id);
            if (wagePayroll == null) return ApiResult.Failure("工资单信息不存在，请先申请工资单");
            wagePayrollService.Verify(wagePayroll);
            var account = projectSpecialAccountService.GetProjectSpecialAccountByProjectId(wagePayroll.ProjectId).Where(i => i.AccountStatus == BankSpecialAccountConstants.专户状态.正常).FirstOrDefault();
            if (account == null) return ApiResult.Failure("该项目专户不存在，请先完善专户信息");
            await bankTransferService.SubmitBatchSalaryPaymentAsync(wagePayroll, account);
            return ApiResult.Success("提交银行代发单成功");
        }

        /// <summary>
        /// 撤销创建的银行代发
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut("payment/undo/{id}")]
        public async Task<ApiResult> UnDoPaymentWages(int id)
        {
            WagePayroll entity = wagePayrollService.Get(id) ?? throw new BusinessException("工资单不存在");

            #region 验证数据权限

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 验证数据权限

            WagePayroll? wagePayroll = wagePayrollService.Get(id);
            if (wagePayroll == null) return ApiResult.Failure("工资单信息不存在，请先申请工资单");
            await bankTransferService.UnDoBatchSalaryPaymentAsync(wagePayroll);
            return ApiResult.Success("撤销银行代发单成功");
        }
    }
}